package com.yuexunit.account.service;

import java.util.List;

import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Restrictions;

import com.yuexunit.account.action.v2.model.AccountAlias;
import com.yuexunit.account.action.v2.model.AccountNameType;
import com.yuexunit.account.action.v2.model.Session;
import com.yuexunit.account.action.v2.model.VerifyCodeType;
import com.yuexunit.account.entity.AccountEntity;
import com.yuexunit.account.entity.AccountNameEntity;
import com.yuexunit.account.entity.AccountPasswordEntity;
import com.yuexunit.account.entity.AccountSessionEntity;
import com.yuexunit.core.exception.DuplicateException;
import com.yuexunit.core.exception.InvalidPasswordException;
import com.yuexunit.core.exception.NoPermissionException;
import com.yuexunit.core.exception.NotFoundException;
import com.yuexunit.core.exception.UnloginException;
import com.yuexunit.inject.ComponentFactory;
import com.yuexunit.inject.annotation.Implements;
import com.yuexunit.persist.PersistUtil;
import com.yuexunit.persist.annotation.Transactional;
import com.yuexunit.util.DateUtil;
import com.yuexunit.util.StringUtil;


/**
 * @author zhangkai
 * revised by wangjinshui
 */
@Implements(AccountService.class)
public class AccountServiceImpl implements AccountService {
	private VerifyCodeService verifyCodeService = ComponentFactory.loadClass(VerifyCodeService.class);
	
	@Override
	public void sendVerifyCodeBySms(VerifyCodeType type, String mobile, String idCardNo) {
		if (type.equals(VerifyCodeType.REGISTER)) {
			checkAccountExisted(idCardNo, mobile);
		} else if (type.equals(VerifyCodeType.FOGET_PASSWORD)) {
			checkIdCardMatchMobile(idCardNo, mobile);
		}
		
		verifyCodeService.sendVerifyCode(type, mobile);
	}
	
	private void checkAccountExisted(String idCardNo, String mobile) {
		checkAccountExisted(idCardNo, mobile, "");
	}
	
	private void checkIdCardMatchMobile(String idCardNo, String mobile) {
		DetachedCriteria cri = DetachedCriteria.forClass(AccountEntity.class);
		cri.add(Restrictions.eq("idCardNo", idCardNo))
			.add(Restrictions.eq("mobile", mobile))
			;
		
		List<AccountEntity> accountList = PersistUtil.get(cri);
		if (accountList.isEmpty()) {
			throw new NotFoundException("手机号码与身份证号不一致");
		} else if (accountList.size() >= 2) {
			throw new NotFoundException("帐号重复，请联系客服更正");
		}
	}
	
	
	@Override
	@Transactional
	public Long createAccount(String fullName, String idCardNo, String mobile, String email, String password) {
		if (StringUtil.isBlank(idCardNo) && StringUtil.isBlank(mobile) && StringUtil.isBlank(email)) {
			throw new IllegalArgumentException("身份证或手机或邮箱其中一项必填");
		}
		checkAccountExisted(idCardNo, mobile, email);
		
		AccountEntity account = new AccountEntity();
		account.setFullName(fullName);
		account.setIdCardNo(idCardNo);
		account.setMobile(mobile);
		account.setEmail(email);
		account.initCreateDate();
		PersistUtil.insert(account);
		
		Long accountId = account.getAccountId();
		AccountPasswordEntity accountPassword = new AccountPasswordEntity(accountId, password);
		PersistUtil.insert(accountPassword);
		
		if (StringUtil.isNotBlank(idCardNo)) {
			AccountNameEntity accountNameIdCardNo = new AccountNameEntity();
			accountNameIdCardNo.setAccountId(accountId);
			accountNameIdCardNo.setAccountName(idCardNo);
			accountNameIdCardNo.setType(AccountNameType.ID_CARD_NO);
			accountNameIdCardNo.initCreateDate();
			PersistUtil.insert(accountNameIdCardNo);
		}
		
		if (StringUtil.isNotBlank(mobile)) {
			AccountNameEntity accountNameMobile = new AccountNameEntity();
			accountNameMobile.setAccountId(accountId);
			accountNameMobile.setAccountName(mobile);
			accountNameMobile.setType(AccountNameType.MOBILE);
			accountNameMobile.initCreateDate();
			PersistUtil.insert(accountNameMobile);
		}
		
		if (StringUtil.isNotBlank(email)) {
			AccountNameEntity accountNameEmail = new AccountNameEntity();
			accountNameEmail.setAccountId(accountId);
			accountNameEmail.setAccountName(email);
			accountNameEmail.setType(AccountNameType.EMAIL);
			accountNameEmail.initCreateDate();
			PersistUtil.insert(accountNameEmail);
		}
		
		return accountId;
	}
	
	private void checkAccountExisted(String idCardNo, String mobile, String email) {
		DetachedCriteria cri = DetachedCriteria.forClass(AccountNameEntity.class);
		cri.add(Restrictions.or(
				Restrictions.eq("accountName", idCardNo), 
				Restrictions.eq("accountName", mobile), 
				Restrictions.eq("accountName", email)));
		
		List<AccountNameEntity> accountNameList = PersistUtil.get(cri);
		for (AccountNameEntity each : accountNameList) {
			if (each.getAccountName().equals(idCardNo)) {
				throw new DuplicateException("身份证号已经注册");
			} else if (each.getAccountName().equals(mobile)) {
				throw new DuplicateException("手机号码已经注册");
			} else if (each.getAccountName().equals(email)) {
				throw new DuplicateException("邮箱已经注册");
			}
		}
	}
	
    @Override
    public Session login(String accountName, String password, String appUuid, Integer appRoleType) {
    	Long accountId = inquireAccountName(accountName).getAccountId();
        AccountPasswordEntity accountPassword = inquireAccountPassword(accountId);
        
        if (!accountPassword.correctPassword(password)) {
            throw new InvalidPasswordException("帐号或密码错误，请重新输入");
        }
        
        if (accountPassword.getEnabled().equals(Boolean.FALSE)) {
        	throw new NoPermissionException("帐号已冻结，请联系客服");
        }
        
        AccountSessionEntity accountSession = retrieveAccountSession(accountId, appUuid, appRoleType);
        return accountSession.toSession();
    }

    @Override
    public Session loginById(Long accountId, String appUuid, Integer appRoleType) {
        AccountSessionEntity accountSession = retrieveAccountSession(accountId, appUuid, appRoleType);
        return accountSession.toSession();
    }
    
    private AccountNameEntity inquireAccountName(String accountName) {
        DetachedCriteria dc = DetachedCriteria.forClass(AccountNameEntity.class);
        dc.add(Restrictions.eq("accountName", accountName));
        
        List<AccountNameEntity> accountNameList = PersistUtil.get(dc);
        if (accountNameList.isEmpty()) {
        	throw new NotFoundException("帐号不存在：" + accountName);
        }
        
        return accountNameList.get(0);
    }
    
    private AccountPasswordEntity inquireAccountPassword(Long accountId) {
    	AccountPasswordEntity accountPassword = PersistUtil.get(AccountPasswordEntity.class, accountId);
        if (accountPassword == null) {
            throw new NotFoundException("帐号密码不存在:" + accountId);
        }

        return accountPassword;
    }
    
    @Transactional
    public AccountSessionEntity retrieveAccountSession(Long accountId, String appUuid, Integer appRoleType) {
        List<AccountSessionEntity> sessionList = inquireAccountSessionList(accountId, appUuid, appRoleType);

        AccountSessionEntity accountSession = null;
        if (sessionList.isEmpty()) {
            accountSession = new AccountSessionEntity(accountId, appUuid, appRoleType);

            PersistUtil.insert(accountSession);
        } else {
            accountSession = sessionList.get(0);
            if (accountSession.expired()) {
                accountSession.initExpireDate();
                accountSession.initCreateDate();

                PersistUtil.update(accountSession);
            }
        }

        return accountSession;
    }
    
    private List<AccountSessionEntity> inquireAccountSessionList(Long accountId, String appUuid, Integer appRoleType) {
        DetachedCriteria criteria = DetachedCriteria.forClass(AccountSessionEntity.class);
        criteria.add(Restrictions.eq("accountId", accountId))
        	.add(Restrictions.eq("appUuid", appUuid))
        	.add(Restrictions.eq("appRoleType", appRoleType))
        	;
        List<AccountSessionEntity> sessionList = PersistUtil.get(criteria);

        return sessionList;
    }
    
    private List<AccountSessionEntity> inquireAccountSessionList(Long accountId) {
        DetachedCriteria criteria = DetachedCriteria.forClass(AccountSessionEntity.class);
        criteria.add(Restrictions.eq("accountId", accountId))
        	;
        List<AccountSessionEntity> sessionList = PersistUtil.get(criteria);

        return sessionList;
    }
    
    @Override
    @Transactional
    public Session updatePassword(String sessionUuid, String oldPassword, String newPassword) {
    	AccountSessionEntity oldSession = inquireAccountSession(sessionUuid);
        AccountPasswordEntity accountPassword = inquireAccountPassword(oldSession.getAccountId());
        if (!accountPassword.correctPassword(oldPassword)) {
            throw new InvalidPasswordException("旧密码错误，请重新输入");
        }
        
        updatePassword(accountPassword, newPassword);
        
        AccountSessionEntity newSession = new AccountSessionEntity(oldSession.getAccountId(), oldSession.getAppUuid(), oldSession.getAppRoleType());
        PersistUtil.insert(newSession);
        
        return newSession.toSession();
    }

    
    @Transactional
    public void deleteSession(Long accountId) {
        List<AccountSessionEntity> sessionList = inquireAccountSessionList(accountId);
        for (AccountSessionEntity each : sessionList) {
            PersistUtil.delete(each);
        }
    }


    @Override
    @Transactional
    public void resetPassword(Long accountId) {
        AccountPasswordEntity accountPassword = inquireAccountPassword(accountId);
        accountPassword.initPasswordSalted("123456");
        accountPassword.initCreateDate();
        PersistUtil.update(accountPassword);
        
        deleteSession(accountId);
    }


    @Override
    @Transactional
    public void disableAccount(Long accountId) {
    	AccountPasswordEntity accountPassword = inquireAccountPassword(accountId);
    	accountPassword.setEnabled(Boolean.FALSE);
    	accountPassword.initCreateDate();
    	PersistUtil.update(accountPassword);
    	
    	deleteSession(accountId);
    }

    
    @Override
    @Transactional
    public void enableAccount(Long accountId) {
    	AccountPasswordEntity accountPassword = inquireAccountPassword(accountId);
    	accountPassword.setEnabled(Boolean.TRUE);
    	accountPassword.initCreateDate();
    	PersistUtil.update(accountPassword);
    }

    
	@Override
	public AccountEntity inquireAccountDetail(Long accountId) {
		AccountEntity account = PersistUtil.get(AccountEntity.class, accountId);
		if (account == null) {
			throw new NotFoundException("帐号不存在");
		}
		
		return account;
	}
    
	
	@Override
	public AccountEntity inquireAccountDetail(String sessionUuid) {
		AccountSessionEntity session = inquireAccountSession(sessionUuid);
		
		AccountEntity account = inquireAccountDetail(session.getAccountId());
		account.setAppRoleType(session.getAppRoleType());
		return account;
	}
	
	private AccountSessionEntity inquireAccountSession(String sessionUuid) {
		AccountSessionEntity session = PersistUtil.get(AccountSessionEntity.class, sessionUuid);
		
		if (session == null) {
			throw new UnloginException("会话不存在");
		}
		
		return session;
	}
    
	
	@Override
	public void forgetPassword(String idCardNo, String mobile, String verifyCode, String newPassword) {
		checkIdCardMatchMobile(idCardNo, mobile);
		verifyCodeService.checkVerifyCode(VerifyCodeType.FOGET_PASSWORD, mobile, verifyCode);
		
		AccountNameEntity nameEntity = inquireAccountName(mobile);
		AccountPasswordEntity accountPassword = inquireAccountPassword(nameEntity.getAccountId());
		updatePassword(accountPassword, newPassword);
	}
	
	@Transactional
    public void updatePassword(AccountPasswordEntity accountPassword, String newPassword) {
    	accountPassword.initPasswordSalted(newPassword);
        accountPassword.initCreateDate();
        PersistUtil.update(accountPassword);
        
        deleteSession(accountPassword.getAccountId());
    }

	
	@Override
	public void changeMobileSms(Long accountId, String oldPassword, String newMobile) {
		AccountPasswordEntity accountPassword = inquireAccountPassword(accountId);
		if (!accountPassword.correctPassword(oldPassword)) {
			throw new NoPermissionException("密码错误");
		}
		
		verifyCodeService.sendVerifyCode(VerifyCodeType.CHANGE_MOBILE, newMobile);
	}

	
	@Override
	public void changeMobile(Long accountId, String oldPassword, String newMobile, String verifyCode) {
		AccountPasswordEntity accountPassword = inquireAccountPassword(accountId);
		if (!accountPassword.correctPassword(oldPassword)) {
			throw new NoPermissionException("密码错误");
		}
		
		verifyCodeService.checkVerifyCode(VerifyCodeType.CHANGE_MOBILE, newMobile, verifyCode);
		
		updateAccountNameMobile(accountId, newMobile);
	}
	
	@Transactional
	public void updateAccountNameMobile(Long accountId, String mobile) {
		AccountNameEntity accountName = inquireAccountName(accountId, AccountNameType.MOBILE);

		AccountEntity account = inquireAccountDetail(accountId);
		account.setMobile(mobile);
		PersistUtil.update(account);
		
		if (accountName == null) {
			accountName = new AccountNameEntity();
			accountName.setAccountId(accountId);
			accountName.setType(AccountNameType.MOBILE);
			accountName.setAccountName(mobile);
			accountName.setCreateDate(DateUtil.now());
			
			PersistUtil.insert(accountName);
		} else {
			accountName.setAccountName(mobile);
			accountName.setCreateDate(DateUtil.now());
			
			PersistUtil.update(accountName);
		}
	}
	
	private AccountNameEntity inquireAccountName(Long accountId, AccountNameType type) {
		List<AccountNameEntity> accountNameList = inquireAccountNameList(accountId);
		
		for (AccountNameEntity each : accountNameList) {
			if (each.getType().equals(type)) {
				return each;
			}
		}
		
		return null;
    }

	@Override
	public Long inquireAccountId(String sessionUuid) {
		AccountSessionEntity session = inquireAccountSession(sessionUuid);
		return session.getAccountId();
	}

	@Override
	@Transactional
	public void logout(String sessionUuid) {
		AccountSessionEntity session = inquireAccountSession(sessionUuid);
		PersistUtil.delete(session);
	}

	@Override
	public List<AccountNameEntity> inquireAccountNameList(Long accountId) {
		DetachedCriteria dc = DetachedCriteria.forClass(AccountNameEntity.class);
		dc.add(Restrictions.eq("accountId", accountId));
        
        List<AccountNameEntity> accountNameList = PersistUtil.get(dc);
        return accountNameList;
	}

	@Override
	@Transactional
	public void deleteAccountName(Long accountId, List<String> accountNameList) {
		List<AccountNameEntity> existedList = inquireAccountNameList(accountId);
		for (AccountNameEntity each : existedList) {
			if (accountNameList.contains(each.getAccountName())) {
				PersistUtil.delete(each);
			}
		}
	}

	@Override
	@Transactional
	public void updateAccountName(AccountAlias accountAlias) {
		AccountNameEntity existed = inquireAccountName(accountAlias.getAccountId(), accountAlias.getType());
		if (existed != null) {
			existed.setAccountName(accountAlias.getAccountName());
			existed.setCreateDate(DateUtil.now());
			PersistUtil.update(existed);
		} else {
			existed = new AccountNameEntity();
			existed.setAccountId(accountAlias.getAccountId());
			existed.setAccountName(accountAlias.getAccountName());
			existed.setType(accountAlias.getType());
			existed.setCreateDate(DateUtil.now());
			
			PersistUtil.insert(existed);
		}
	}
}
